package org.openmuc.openiec61850.sample;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;

import org.openmuc.jositransport.TConnection;
import org.openmuc.openiec61850.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author Stefan Feuerhahn
 *
 */
public class Sample2Client implements ClientEventListener {

    private final static Logger logger = LoggerFactory.getLogger(SampleClient.class);

    public static void main(String[] args) throws ServiceError, IOException {

        List<InetAddress> address = new ArrayList<InetAddress>();
        address.add(InetAddress.getByName("10.111.55.126"));
        address.add(InetAddress.getByName("10.111.55.124"));
        final int remotePort = 102;

        ClientSap clientSap = new ClientSap();
        // alternatively you could use ClientSap(SocketFactory factory) to e.g. connect using SSL

        // optionally you can set some association parameters (but usually the default should work):
        // clientSap.setTSelRemote(new byte[] { 0, 1 });
        // clientSap.setTSelLocal(new byte[] { 0, 0 });

        Sample1Client eventHandler = new Sample1Client();
        List<ClientAssociation> association = new ArrayList<ClientAssociation>();

        logger.info("Attempting to connect to server " + address.toString() + " on port " + remotePort);
        try {
            for(Iterator<InetAddress> it = address.iterator(); it.hasNext();){
                association.add(clientSap.associate(it.next(), remotePort, null, eventHandler));
            }
            /*
            for(int i = 0; i < address.size(); i++){
                association.add(clientSap.associate(address.get(i), remotePort, null, eventHandler));
            }*/
        } catch (IOException e) {
            // an IOException will always indicate a fatal exception. It indicates that the association was closed and
            // cannot be recovered. You will need to create a new association using ClientSap.associate() in order to
            // reconnect.
            logger.error("Error connecting to server: " + e.getMessage());
            return;
        }

        ServerModel serverModel;
        try {
            // requestModel() will call all GetDirectory and GetDefinition ACSI services needed to get the complete
            // server model
            serverModel = association.get(0).retrieveModel();
            for(int i = 1; i < association.size(); i++){
                serverModel.addModel(association.get(i).retrieveModel());
            }
        } catch (ServiceError e) {
            logger.error("Service Error requesting model.", e);
            for(int i = 0; i < association.size(); i++)
                association.get(i).close();
            return;
        } catch (IOException e) {
            logger.error("Fatal IOException requesting model.", e);
            return;
        }

        //System.out.println(serverModel.toString());
/*
        // instead of calling retrieveModel you could read the model directly from an SCL file:
        try {
            serverModel = association.getModelFromSclFile("F://PCS.scd");
        } catch (SclParseException e1) {
        logger.error("Error parsing SCL file.", e1);

        return;
        }
*/
        // get the values of all data attributes in the model:
        for(int i = 0; i < association.size(); i++){
            try {
                association.get(i).getAllDataValues();
            } catch (Exception ex) {

            }

        }

        for (BasicDataAttribute bda : serverModel.getBasicDataAttributes()) {
            logger.info("BDA: " + bda);
        }

        for (Urcb urcb : serverModel.getUrcbs()) {
            logger.info("URCB: " + urcb);
        }

        for (Brcb brcb : serverModel.getBrcbs()) {
            logger.info("BRCB: " + brcb);
        }

        // example for writing a variable:
        //FcModelNode modCtlModel = (FcModelNode) serverModel.findModelNode("ied1lDevice1/CSWI1.Mod.ctlModel", Fc.CF);
        //association.setDataValues(modCtlModel);

        // example for enabling reporting
        Urcb urcb = serverModel.getUrcb("CSI200EA_380_6CTRL/LLN0$RP$urcbDOST01");
        if (urcb == null) {
            logger.error("ReportControlBlock not found");
        }
        else {
            for(int i = 0; i < association.size(); i++) {
                association.get(i).getRcbValues(urcb);
                logger.info("urcb name: " + urcb.getName());
                logger.info("RptId: " + urcb.getRptId());
                logger.info("RptEna: " + urcb.getRptEna().getValue());
                association.get(i).reserveUrcb(urcb);
                association.get(i).enableReporting(urcb);
                association.get(i).startGi(urcb);
                association.get(i).disableReporting(urcb);
                association.get(i).cancelUrcbReservation(urcb);
            }
        }

        // example for reading a variable:
        FcModelNode totW = (FcModelNode) serverModel.findModelNode("CSI200EA_380_6MEAS/AI1MMXN11.Vol", Fc.MX);
        BdaFloat32 totWmag = (BdaFloat32) totW.getChild("mag").getChild("f");
        BdaTimestamp totWt = (BdaTimestamp) totW.getChild("t");
        BdaQuality totWq = (BdaQuality) totW.getChild("q");


        while (true) {
            for(int i = 0; i < association.size(); i++) {
                association.get(i).getDataValues(totW);
            }
            logger.info("got totW: mag " + totWmag.getFloat() + ", time " + totWt.getDate() + ", quality "
                    + totWq.getValidity());

            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
            }

        }

    }

    @Override
    public void newReport(TConnection tConnection, Report report) {
        logger.info("got report with dataset ref: " + report.getDataSet().getReferenceStr());
        // do something with the report

    }

    @Override
    public void associationClosed(IOException e) {
        logger.info("Association was closed");
    }

}
